package com.k8.registry.config;

import com.k8.common.event.EventDeployer;
import com.k8.common.factroy.BeanFactory;
import com.k8.common.factroy.loader.ExtensionDirector;
import com.k8.registry.NotifyListener2;
import com.k8.rpc.Protocol;
import com.k8.rpc.config.ProtocolConfig;
import com.k8.rpc.config.RegistryConfig;
import com.k8.rpc.expoter.Exporter;
import com.k8.rpc.invoke.Invoker;
import com.k8.rpc.metadata.AppMetaData;
import com.k8.rpc.registry.Registry;
import com.k8.rpc.url.ApiSignature;
import com.k8.rpc.url.RegistrySignature;
import com.k8.rpc.url.URL;
import com.k8.rpc.util.RpcUtils;
import com.k8.rpc.util.URLUtils;

import java.util.LinkedList;
import java.util.List;

import static com.k8.common.constants.CommonKeyConstants.*;
import static com.k8.common.constants.CommonKeyConstants.RegistryKeyConstant.REGISTRY_KEY;
import static com.k8.common.constants.CommonKeyConstants.RpcKeyConstant.API_SIGNATURE_KEY;


/**
 * 一，服务导出
 * 1.1配置检测与填充
 * 1.2将元数据初始化好，每个唯一{服务实现类，协议}组合都对应着一个配置
 * 1.3 服务导出
 * 1.4 服务注册
 * 二，事件发布机制
 * 2.1 服务准备成功添加服务导出事件
 * 2.2 exporter服务导出事件，预留动态导出扩展点，每一个exporter都应对应生成监听器，当接收到导出事件时，便判端是否是自己对应的服务的，如果是则重新导出
 */

/**
 * dubbo 在服务导出的时候依赖于RegistryProtocol将服务元数据配置注册或路由到注册中心或元数据中心
 * 而DubboProtocol则只负责服务导出，先导出服务开启后再通过RegistryProtocol进行注册
 * 后续dubbo会逐渐替代淘汰RegistryProtocol，且dubbo对Protocol一层套一层，阅读困难，debug不方便
 * K8将高度依赖于Aop，除了Invoker所需增强功能，如metrics
 */
//不同协议对于不同服务的实现param等是不同的
//同样registerUrl也有不同，针对不同服务实现
//但是registryConfig是注册中心的相关配置，开启了就不会再变，这个没必要解析多次
//todo 创建时初始化好配置,此外应该交由Spi的容器管理
public class ServiceBase<T> extends AbstractServiceConfig<T> {
    private final List<Exporter<?>> exporters = new LinkedList<>();
    private final List<URL> protocolUrls;
    private final Protocol adaptiveProtocol;
    private final BeanFactory beanFactory;
    private final EventDeployer eventDeployer;

    public ServiceBase(BeanFactory beanFactory, URL systemConfigUrl, List<URL> registryConfigUrls, List<URL> protocolUrls) {
        super(systemConfigUrl, registryConfigUrls);
        this.beanFactory = beanFactory;
        ExtensionDirector extensionDirector = beanFactory.getExtensionDirector();
        this.adaptiveProtocol = extensionDirector.getExtensionLoader(Protocol.class).getAdaptiveExtension();
        this.eventDeployer = beanFactory.getBean(EventDeployer.class);
        if (this.eventDeployer == null) throw new IllegalStateException("EventDeployer is not register in beanFactory");
        this.protocolUrls = protocolUrls;
    }


    /**
     * //如果是protocolUrl，path一般由 /服务接口全限定名:group:version（不使用实现类的全限定名）
     * 导出服务只导出到远程，支持网络服务调用，不支持本地服务导出以供本地进程直接调用（区别于dubbo，没时间做了）
     * 先进行服务导出，支持调用由exporter持有invoker，支持根据注册中心变化关闭导出和再次导出，
     * protocolConfig应该可以对protocolUrl进行更细粒度的配置
     * 调用时是通过模板invocation{@link com.k8.rpc.invoke.invocation.RpcInvocationTemplate} 倒排索引映射到exporter
     */
    public void export() {
        for (URL protocolUrl : protocolUrls) {
            protocolUrl.setPath(RpcUtils.interfaceToPath(type, group, version));
            doExportByUrl(protocolUrl);
        }
    }

    public void exportApp(AppMetaData appMetaData) {
        for (URL originRegisterConfigUrl : registryConfigUrls) {
            URL registerConfigUrl = URLUtils.copy(originRegisterConfigUrl);
            Registry registry = RpcUtils.getRegistry(registerConfigUrl, beanFactory);
            registry.registerInstance(appMetaData, registerConfigUrl);
        }
    }


    /**
     * refer指在创建Invoker，provider和consumer都需要使用，所以需要指定side，并且provider的invoker还需要一些如instance等东西
     */


    //对每一个protocol进行注册
    private void doExportByUrl(URL protocolUrl) {
        //todo 依次导出服务
        //创建Invoker
        Invoker<T> invoker = createProviderInvoker(protocolUrl);
        //对所有注册中心进行服务导出
        for (URL originRegisterConfigUrl : registryConfigUrls) {
            URL registerConfigUrl = URLUtils.copy(originRegisterConfigUrl);
            //用于adaptive选择protocol
            registerConfigUrl.setParameter(EXPORTER_KEY, protocolUrl.getProtocol());
            registerConfigUrl.putAttribute(EXPORTER_KEY, protocolUrl);
            ApiSignature apiSignature = new ApiSignature(protocolUrl.getProtocol(), protocolUrl.getPath(), registerConfigUrl.getPath());
            registerConfigUrl.putAttribute(API_SIGNATURE_KEY, apiSignature);
            Exporter<T> exporter = adaptiveProtocol.export(invoker, registerConfigUrl);
            exporters.add(exporter);
        }
    }

    //取缔了dubbo的stub机制的invoker扩展方法，使用更为灵活的动态代理方式
    private Invoker<T> createProviderInvoker(URL protocolUrl) {
        URL url = new URL(protocolUrl);
        return RpcUtils.buildProviderInvoker(url, beanFactory, type, instance);
    }

    /**
     * 暂时不用，只有消费之需要监听器
     */
    private void exportRemote(Invoker<T> invoker, URL registerUrl) {
        //此时已经开始异步导出服务了
        Exporter<T> exporter = adaptiveProtocol.export(invoker, registerUrl);
        URL registryConfigUrl = (URL) registerUrl.getAttribute(REGISTRY_KEY);
        ApiSignature apiSignature = (ApiSignature) registerUrl.getAttribute(API_SIGNATURE_KEY);
        RegistrySignature registrySignature = new RegistrySignature(registryConfigUrl);
        String eventSignature = RpcUtils.getExporterSignature(apiSignature, registrySignature);
        NotifyListener2 notifyListener2 = new NotifyListener2(exporter, registerUrl, eventSignature, adaptiveProtocol);
        eventDeployer.addListener(notifyListener2);
    }
}
